package com.olympus.hermes.gateway.config;

import com.olympus.hermes.gateway.filter.HeaderWriterFilter;
import com.olympus.hermes.gateway.handler.AuthenticationSuccessHandler;
import com.olympus.hermes.gateway.handler.CustomAuthenticationFailureHandler;
import com.olympus.hermes.gateway.handler.RestfulAccessDeniedHandler;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.client.web.server.WebSessionServerOAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
import org.springframework.security.oauth2.server.resource.authentication.JwtReactiveAuthenticationManager;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
import org.springframework.web.server.WebFilter;


/**
 * @author 米奇
 * @since 2020.08.10
 */
@Configuration
@EnableWebFluxSecurity
public class ServerHttpSecurityConfig {

    private final AccessManager accessManager;

    private final RestfulAccessDeniedHandler restfulAccessDeniedHandler;

    private final AuthenticationSuccessHandler authenticationSuccessHandler;

    private final CustomAuthenticationFailureHandler customAuthenticationFailureHandler;

    private final CustomTokenAuthenticationConverter customTokenAuthenticationConverter;

    @Value(value = "${micro.server.auth}")
    private String auth;


    public ServerHttpSecurityConfig(AccessManager accessManager,
                                    RestfulAccessDeniedHandler restfulAccessDeniedHandler,
                                    AuthenticationSuccessHandler authenticationSuccessHandler,
                                    CustomAuthenticationFailureHandler customAuthenticationFailureHandler,
                                    CustomTokenAuthenticationConverter customTokenAuthenticationConverter) {
        this.customAuthenticationFailureHandler = customAuthenticationFailureHandler;
        this.authenticationSuccessHandler = authenticationSuccessHandler;
        this.accessManager = accessManager;
        this.restfulAccessDeniedHandler = restfulAccessDeniedHandler;
        this.customTokenAuthenticationConverter = customTokenAuthenticationConverter;
    }

    public WebFilter authenticationWebFilter() {
        JwtReactiveAuthenticationManager jwtReactiveAuthenticationManager = new JwtReactiveAuthenticationManager(new NimbusReactiveJwtDecoder(auth + "/rsa/publicKey"));
        AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(jwtReactiveAuthenticationManager);
        authenticationWebFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
        authenticationWebFilter.setAuthenticationFailureHandler(customAuthenticationFailureHandler);
        authenticationWebFilter.setServerAuthenticationConverter(customTokenAuthenticationConverter);
        return authenticationWebFilter;
    }

    @Bean
    public ServerOAuth2AuthorizedClientRepository serverOAuth2AuthorizedClientRepository() {
        return new WebSessionServerOAuth2AuthorizedClientRepository();
    }

    @Bean
    public SecurityWebFilterChain webFluxSecurityFilterChain(ServerHttpSecurity http) {
        http
                .httpBasic().disable()
                .csrf().disable()
                .authorizeExchange()
                .pathMatchers(HttpMethod.OPTIONS)
                .permitAll()
                .pathMatchers("/oauth/token")
                .permitAll()
                .anyExchange().access(accessManager)
                .and()
                .exceptionHandling()
                .accessDeniedHandler(restfulAccessDeniedHandler)
                .and()
                //添加请求头
                .addFilterAfter(HeaderWriterFilter.getInstance(), SecurityWebFiltersOrder.HTTP_HEADERS_WRITER)
                //oauth2认证过滤器
                .addFilterAt(authenticationWebFilter(), SecurityWebFiltersOrder.AUTHENTICATION);
        return http.build();
    }

}
